#include "lioimg.h"

namespace Lio {

const double COLOR_TRANS_TABLE[][2][3][3]=
{
	{// RGB_REAL_D65
		{
			{0.606881,0.173505,0.200336},
			{0.298912,0.586611,0.114478},
			{0.000000,0.066097,1.116157}
		},
		{
			{ 1.910028,-0.532464,-0.288214},
			{-0.984648, 1.999131,-0.028308},
			{ 0.058309,-0.118385, 0.897608}
		}
	},
	{// RGB_CCIR_601
		{
			{ 0.430574, 0.341550, 0.178325},
			{ 0.222015, 0.706655, 0.071330},
			{ 0.020183, 0.129553, 0.939180}
		},
		{
			{ 3.063219,-1.393326,-0.475801},
			{-0.969245, 1.875968, 0.041555},
			{ 0.067872,-0.228833, 1.069251}
		}
	},
	{// RGB_Adobe_D65
		{
			{ 0.576700, 0.297361, 0.0270328},
			{ 0.185556, 0.627355, 0.0706879},
			{ 0.188212, 0.0752847, 0.991248}
		},
		{
			{ 2.04148,-0.969258, 0.0134455},
			{-0.564977, 1.87599,-0.118373},
			{-0.344713, 0.0415557,1.01527}
		}
	},
	{// RGB_Apple_D65
		{
			{ 0.449695, 0.244634, 0.0251829},
			{ 0.316251, 0.672034, 0.141184},
			{ 0.18452, 0.0833318, 0.922602}
		},
		{
			{ 2.95176,-1.0851, 0.0854804},
			{-1.28951, 1.99084,-0.269456},
			{-0.47388, 0.0372023, 1.09113}
		}
	},
	{// RGB_Best_D50
		{
			{ 0.632670, 0.228457, 0.000000},
			{ 0.204556, 0.737352, 0.00951424},
			{ 0.126995, 0.0341908, 0.815696}
		},
		{
			{ 1.75526,-0.544134, 0.00634681},
			{-0.483679, 1.50688,-0.0175762},
			{-0.253000, 0.0215528, 1.22570}
		}
	},
	{// RGB_Beta_D50
		{
			{ 0.671254, 0.303273, 0.000000},
			{ 0.174583, 0.663786, 0.040701},
			{ 0.118383, 0.0329413, 0.784509}
		},
		{
			{ 1.68323,-0.771023, 0.0400012},
			{-0.428236, 1.70656,-0.0885376},
			{-0.236018, 0.0446899, 1.27236}
		}
	},
	{// RGB_Bruce_D65
		{
			{ 0.467384, 0.240995, 0.0219086},
			{ 0.294454, 0.683554, 0.0736135},
			{ 0.188629, 0.0754517, 0.993447}
		},
		{
			{ 2.74566,-0.969257, 0.0112707},
			{-1.13589, 1.87599,-0.113959},
			{-0.435057, 0.0415557, 1.01311}
		}
	},
	{// RGB_CIE_E
		{
			{ 0.488718, 0.176204, 0.000000},
			{ 0.310680, 0.812985, 0.0102048},
			{ 0.200602, 0.0108109, 0.989795}
		},
		{
			{ 2.37067,-0.513885, 0.00529818},
			{-0.900040, 1.42530,-0.0146949},
			{-0.470634, 0.0885814, 1.00940}
		}
	},
	{// RGB_ColorMatch_D50
		{
			{ 0.509344, 0.274884, 0.0242545},
			{ 0.320907, 0.658132, 0.108782},
			{ 0.133969, 0.0669845, 0.692174}
		},
		{
			{ 2.64229,-1.11198, 0.0821698},
			{-1.22343, 2.05902,-0.280725},
			{-0.393014, 0.0159614, 1.45599}
		}
	},
	{// RGB_DonRGB4_D50
		{
			{ 0.645771, 0.278350, 0.00371134},
			{ 0.193351, 0.687970, 0.0179862},
			{ 0.125098, 0.0336802, 0.803513}
		},
		{
			{ 1.76039,-0.712629, 0.00782072},
			{-0.488120, 1.65274,-0.0347411},
			{-0.253613, 0.0416715, 1.24477}
		}
	},
	{// RGB_ECI_D50
		{
			{ 0.650204, 0.320250, 0.000000},
			{ 0.178077, 0.602071, 0.0678390},
			{ 0.135938, 0.0776791, 0.757371}
		},
		{
			{ 1.78276,-0.959362, 0.0859318},
			{-0.496985, 1.94780,-0.174467},
			{-0.269010,-0.0275807, 1.32283}
		}
	},
	{// RGB_EktaSpacePS5_D50
		{
			{ 0.593891, 0.260629, 0.000000},
			{ 0.272980, 0.734946, 0.0419970},
			{ 0.0973486, 0.00442493, 0.783213}
		},
		{
			{ 2.00438,-0.711029, 0.0381263},
			{-0.730484, 1.62021,-0.0868780},
			{-0.245005, 0.0792227, 1.27254}
		}
	},
	{// RGB_NTSC_C
		{
			{ 0.606734, 0.298839, 0.000000},
			{ 0.173564, 0.586811, 0.0661196},
			{ 0.200112, 0.114350, 1.11491}
		},
		{
			{ 1.91049,-0.984310, 0.0583744},
			{-0.532592, 1.99845,-0.118518},
			{-0.288284,-0.0282980, 0.898611}
		}
	},
	{// RGB_PAL_SECAM_D65
		{
			{ 0.430587, 0.222021, 0.0201837},
			{ 0.341545, 0.706645, 0.129551},
			{ 0.178336, 0.0713342, 0.939234}
		},
		{
			{ 3.06313,-0.969258, 0.0678674},
			{-1.39328, 1.87599,-0.228821},
			{-0.475788, 0.0415557, 1.06919}
		}
	},
	{// RGB_ProPhoto_D50
		{
			{ 0.797675, 0.288040, 0.000000},
			{ 0.135192, 0.711874, 0.000000},
			{ 0.0313534, 0.000086, 0.825210}
		},
		{
			{ 1.34594,-0.544599, 0.000000},
			{-0.255608, 1.50817, 0.000000},
			{-0.0511118, 0.0205351, 1.21181}
		}
	},
	{// RGB_SMPTE_C_D65
		{
			{ 0.393555, 0.212395, 0.0187407},
			{ 0.365253, 0.701049, 0.111932},
			{ 0.191659, 0.0865558, 0.958297}
		},
		{
			{ 3.50570,-1.06906, 0.0563117},
			{-1.73964, 1.97781,-0.196994},
			{-0.544011, 0.0351720, 1.05005}
		}
	},
	{// RGB_sRGB_D65
		{
			{ 0.412424, 0.212656, 0.0193324},
			{ 0.357579, 0.715158, 0.119193},
			{ 0.180464, 0.0721856, 0.950444}
		},
		{
			{ 3.24071,-0.969258, 0.0556352},
			{-1.53726, 1.87599,-0.203996},
			{-0.498571, 0.0415557, 1.05707}
		}
	},
	{// RGB_WideGamut_D50
		{
			{ 0.716105, 0.258187, 0.000000},
			{ 0.100930, 0.724938, 0.0517813},
			{ 0.147186, 0.0168748, 0.773429}
		},
		{
			{ 1.46281,-0.521793, 0.0349342},
			{-0.184062, 1.44724,-0.0968931},
			{-0.274361, 0.0677228, 1.28841}
		}
	}
};
DWORD colorMix(DWORD color1, DWORD color2, double ratio)
{
	mixVector(INT_TO_BYTE(color1),INT_TO_BYTE(color2),ratio);
	return color2;
}
float colorFloatToGray(const float *color, int table)
{
	return dot(COLOR_TRANS_TABLE[table][RGB_TO_XYZ][1],color);
}
double colorMaxSat(float ** rgbData, int count)
{
	double k;
	float *color=rgbData[0];
	double m=(color[0]+color[1]+color[2])/3;
	if(color[0]-m<0)
		k=-m/(color[0]-m);
	else
		k=(1-m)/(color[0]-m);
	double kmin=k;
	if(color[1]-m<0)
		k=-m/(color[1]-m);
	else
		k=(1-m)/(color[1]-m);
	if(kmin>k)
		kmin=k;
	if(color[2]-m<0)
		k=-m/(color[2]-m);
	else
		k=(1-m)/(color[2]-m);
	if(kmin>k)
		kmin=k;
	for(int i=1;i<count;i++)
	{
		color=rgbData[i];
		m=(color[0]+color[1]+color[2])/3;
		if(color[0]-m<0)
			k=-m/(color[0]-m);
		else
			k=(1-m)/(color[0]-m);
		if(kmin>k)
			kmin=k;
		if(color[1]-m<0)
			k=-m/(color[1]-m);
		else
			k=(1-m)/(color[1]-m);
		if(kmin>k)
			kmin=k;
		if(color[2]-m<0)
			k=-m/(color[2]-m);
		else
			k=(1-m)/(color[2]-m);
		if(kmin>k)
			kmin=k;
	}
	return kmin;
}
BOOL colorRGBtoHSL(const float *rgbf, float *hsl)
{
	int maxc,minc;
	float diff;
	if(!rgbf||!hsl)
		return FALSE;
	maxc=colorMax(rgbf);
	if(!rgbf[maxc])
	{
		hsl[HUE]=hsl[SAT]=hsl[LUM]=0;
		return TRUE;
	}
	minc=colorMin(rgbf);
	diff=rgbf[maxc]-rgbf[minc];
	if(diff)
	{
		float hue,sat,lum;
		switch(maxc)
		{
		case BLUE:// B>G>R(3 : 4) ; B>R>G(4 : 5) 
			hue=((rgbf[RED]-rgbf[GREEN])/diff+4)/6; 
			break;
		case GREEN:// G>R>B(1 : 2) ; G>B>R(2 : 3) 
			hue=((rgbf[BLUE]-rgbf[RED])/diff+2)/6;
			break;
		default:// R>B>G(-1 : 0) ; R>G>B(0 : 1) 
			hue=((rgbf[GREEN]-rgbf[BLUE])/diff)/6;
			break;
		}
		if(hue<0)
			hue+=1.0f;
		else if(hue>1.0f)
			hue-=1.0f;
		lum=0.5f*(rgbf[maxc]+rgbf[minc]);
		if(lum>0.5f)
			sat=0.5f*diff/(1-lum);
		else
			sat=0.5f*diff/lum;
		hsl[HUE]=hue;
		hsl[SAT]=sat;
		hsl[LUM]=lum;
	}
	else
	{
		hsl[HUE]=0;
		hsl[SAT]=0;
		hsl[LUM]=rgbf[maxc];
	}
	return TRUE;
}
BOOL colorHSLtoRGB(const float *hsl, float *rgbf)
{
	int ac;
	float red,green,blue,adiv,mink,midk,maxk;
	if(!rgbf||!hsl)
		return FALSE;
	if(!hsl[SAT])
	{
		rgbf[RED]=rgbf[GREEN]=rgbf[BLUE]=hsl[LUM];
		return TRUE;
	}
	if(hsl[LUM]<0.5f)
		maxk=1+hsl[SAT];
	else
		maxk=1+hsl[SAT]/hsl[LUM]-hsl[SAT];
	mink=2-maxk;
	adiv=6*hsl[HUE];
	ac=2*round(3*hsl[HUE]);
	midk=mink+absval(adiv-ac)*(maxk-mink);
	switch(__int(adiv))
	{
	case 1:// G>R>B [1 : 2)
		red=midk;
		green=maxk;
		blue=mink;
		break;
	case 2:// G>B>R [2 : 3)
		red=mink;
		green=maxk;
		blue=midk;
		break;
	case 3:// B>G>R [3 : 4)
		red=mink;
		green=midk;
		blue=maxk;
		break;
	case 4:// B>R>G [4 : 5) 
		red=midk;
		green=mink;
		blue=maxk;
		break;
	case 5:// R>B>G [5 : 6)
		red=maxk;
		green=mink;
		blue=midk;
		break;
	default: // R>G>B [0 : 1) 
		red=maxk;
		green=midk;
		blue=mink;
	}
	rgbf[RED]=hsl[VAL]*red;
	rgbf[GREEN]=hsl[VAL]*green;
	rgbf[BLUE]=hsl[VAL]*blue;
	return TRUE;
}
BOOL colorTrans(float **rgbData, float **outData, int count, int type, int table)
{
	int i,maxc,minc,ac;
	float *color,*out,diff,adiv,mink,midk,maxk;
	float red,green,blue,hue,sat,lumval,gray;
	if(!outData)
		outData=rgbData;
	switch(type)
	{
	case RGB_TO_XYZ:
	case XYZ_TO_RGB:
		for(i=0;i<count;i++)
			mulVector(COLOR_TRANS_TABLE[table][type],rgbData[i],outData[i]);
		break;
	case RGB_TO_CMY:
		break;
	case CMY_TO_RGB:
		break;
	case RGB_INVERT:
	case RGB_NEGATIVE:
		for(i=0;i<count;i++)
		{
			outData[i][0]=1-rgbData[i][0];
			outData[i][1]=1-rgbData[i][1];
			outData[i][2]=1-rgbData[i][2];
		}
		break;
	case RGB_TO_HSV:
	case RGB_TO_HSL:
		for(i=0;i<count;i++)
		{
			color=rgbData[i];
			out=outData[i];
			maxc=colorMax(color);
			if(!color[maxc])
			{
				out[HUE]=out[SAT]=out[LUM]=0;
				continue;
			}
			minc=colorMin(color);
			diff=color[maxc]-color[minc];
			if(diff)
			{
				switch(maxc)
				{
				case BLUE:// B>G>R(3 : 4) ; B>R>G(4 : 5) 
					hue=((color[RED]-color[GREEN])/diff+4)/6; 
					break;
				case GREEN:// G>R>B(1 : 2) ; G>B>R(2 : 3) 
					hue=((color[BLUE]-color[RED])/diff+2)/6;
					break;
				default:// R>B>G(-1 : 0) ; R>G>B(0 : 1) 
					hue=((color[GREEN]-color[BLUE])/diff)/6;
					break;
				}
				if(hue<0)
					hue+=1.0f;
				else if(hue>1.0f)
					hue-=1.0f;
			}
			if(type==RGB_TO_HSV)
			{
				lumval=color[maxc];
				sat=diff/lumval;
			}
			else
			{
				lumval=0.5f*(color[maxc]+color[minc]);
				if(lumval>0.5f)
					sat=0.5f*diff/(1-lumval);
				else
					sat=0.5f*diff/lumval;
			}
			out[HUE]=hue;
			out[SAT]=sat;
			out[LUM]=lumval;
		}
		break;
	case HSV_TO_RGB:
	case HSL_TO_RGB:
		for(i=0;i<count;i++)
		{
			color=rgbData[i];
			out=outData[i];
			if(!color[SAT])
			{
				out[RED]=out[GREEN]=out[BLUE]=color[VAL];
				continue;
			}
			if(type==HSV_TO_RGB)
			{
				maxk=1;
				mink=1-color[SAT];
			}
			else
			{
				if(color[LUM]<0.5f)
					maxk=1+color[SAT];
				else
					maxk=1+color[SAT]/color[LUM]-color[SAT];
				mink=2-maxk;
			}
			adiv=6*color[HUE];
			ac=2*round(3*color[HUE]);
			midk=mink+absval(adiv-ac)*(maxk-mink);
			switch(__int(adiv))
			{
			case 1:// G>R>B [1 : 2)
				red=midk;
				green=maxk;
				blue=mink;
				break;
			case 2:// G>B>R [2 : 3)
				red=mink;
				green=maxk;
				blue=midk;
				break;
			case 3:// B>G>R [3 : 4)
				red=mink;
				green=midk;
				blue=maxk;
				break;
			case 4:// B>R>G [4 : 5) 
				red=midk;
				green=mink;
				blue=maxk;
				break;
			case 5:// R>B>G [5 : 6)
				red=maxk;
				green=mink;
				blue=midk;
				break;
			default: // R>G>B [0 : 1) 
				red=maxk;
				green=midk;
				blue=mink;
			}
			out[RED]=color[VAL]*red;
			out[GREEN]=color[VAL]*green;
			out[BLUE]=color[VAL]*blue;
		}
		break;
	case RGB_TO_GRAY:
		for(i=0;i<count;i++)
		{
			gray=colorFloatToGray(rgbData[i],table);
			initVector(outData[i],gray,gray,gray);
		}
		break;
	default:
		return FALSE;
	}
	return TRUE;
}
void colorGamma(float *rgbf, double gamma, float *out)
{
	if(!out)
		out=rgbf;
	if(gamma>0)
	{
		double den=INV(gamma);
		out[RED]=(float)pow((double)rgbf[RED],den);
		out[GREEN]=(float)pow((double)rgbf[GREEN],den);
		out[BLUE]=(float)pow((double)rgbf[BLUE],den);
	}
	else
	{
		initVector(out);
	}
}
void colorSat(float *rgbf, double kr, double kg, double kb, float *out)
{
	float dc[3]={	float((kr-1)*(2*rgbf[RED]-rgbf[GREEN]-rgbf[BLUE])/3),
					float((kg-1)*(2*rgbf[GREEN]-rgbf[BLUE]-rgbf[RED])/3),
					float((kb-1)*(2*rgbf[BLUE]-rgbf[RED]-rgbf[GREEN])/3)};
	limitVector(dc,0.0f,1.0f);
	addVector(rgbf,dc,out);
}
BOOL colorHisto(float **rgbData, int count, int *histo, int channel, int levels, BOOL clear)
{
	int level;
	if((channel!=RED&&channel!=GREEN&&channel!=BLUE))
		return FALSE;
	if(clear)
		ClearArray(histo,levels);
	for(int i=0;i<count;i++)
	{
		level=round(rgbData[i][channel]*(levels-1));
		histo[level]++;
	}
	return TRUE;
}
BOOL Image::replace(const BYTE *newData)
{
	if(buffer.copy(newData))
	{
		data=buffer;
		return TRUE;
	}
	return FALSE;
}
BOOL Image::setSize(int newWidth, int newHeight, BYTE pixelComp, BOOL clear)
{
	if(newWidth<=0||newHeight<=0)
		return FALSE;
	if(pixelComp != PIXEL_COMP_OLD)
	{
		if(!checkPixelComp(pixelComp))
			return FALSE;
		comps=pixelComp;
	}
	if(!buffer.setSize(newWidth*newHeight*comps,clear))
		return FALSE;
	data=buffer;
	width=newWidth;
	height=newHeight;
	return TRUE;
}
BOOL Image::copy(const Image &image)
{
	if(buffer.copy(image.buffer))
	{
		data=buffer;
		width=image.width;
		height=image.height;
		comps=image.comps;  
		return TRUE;
	}
	return FALSE;
}
BOOL Image::bind(BYTE * newData, int newWidth, int newHeight, int memType, BYTE pixelComp)
{
	if(newWidth<=0||newHeight<=0)
		return FALSE;
	if(pixelComp != PIXEL_COMP_OLD)
	{
		if(!checkPixelComp(pixelComp))
			return FALSE;
		comps=pixelComp;
	}
	else if(!buffer.bind(newData,newWidth*newHeight*comps,memType))
		return FALSE;
	data=buffer;
	width=newWidth;
	height=newHeight;
	return TRUE;
}
void Image::destroy()
{
	buffer.destroy();
	data=NULL;
	width=0;
	height=0;
}
BOOL writeTGA(FILE* fp, Image *image)
{
	BOOL result=FALSE;
	TGAHeader header;
	int imgSize=image->getSize();
	if(fp&&image&&imgSize&&image->comps>=PIXEL_COMP_RGB)
	{
		ClearStruct(header);
		header.m_iImageTypeCode=2;
		header.m_iWidth=(WORD)image->width;
		header.m_iHeight=(WORD)image->height;
		header.m_iColourMapEntrySize=24;
		header.m_iBPP=24;
		if(fwrite(&header,sizeof(TGAHeader),1,fp)==1)
		{
			for(int i=2;i<imgSize;i+=image->comps)
			{
				fputc(image->data[i],fp);
				fputc(image->data[i-1],fp);
				fputc(image->data[i-2],fp);
			}
			result=TRUE;
		}
	}
	return result;		  
}
BOOL readTGA(FILE* fp, Image *image)
{
	int width, height, bpp, i;
	TGAHeader header;
	BOOL result=FALSE;
	if(fp&&image)
	{
		if(fread(&header,sizeof(TGAHeader),1,fp)==1)
		{
			switch(header.m_iImageTypeCode)
			{
				case 2:// rgb or rgba image
				{
					width=header.m_iWidth;
					height=header.m_iHeight;
					bpp=header.m_iBPP;
					if(image->setSize(width,height,(BYTE)bpp/8))
					{
						switch(image->comps)
						{
							case PIXEL_COMP_RGBA:
							{
								for(i=3;i<image->getSize();i+=PIXEL_COMP_RGBA)
								{
									image->data[i]=(BYTE)fgetc(fp);
									image->data[i-1]=(BYTE)fgetc(fp);
									image->data[i-2]=(BYTE)fgetc(fp);
									image->data[i-3]=(BYTE)fgetc(fp);
								}
								result=TRUE;
								break;
							}
							case PIXEL_COMP_RGB:
							{
								for(i=2;i<image->getSize();i+=PIXEL_COMP_RGB)
								{
									image->data[i]=(BYTE)fgetc(fp);
									image->data[i-1]=(BYTE)fgetc(fp);
									image->data[i-2]=(BYTE)fgetc(fp);
								}
								result=TRUE;
								break;
							}
						}
						if(GET_BIT(header.m_ImageDescriptorByte,5))
							image->flipV();
					}
				}
			}
		}
	}
	return result;		  
}
BOOL writeBitmap(FILE *fp, Image *image)
{
    DWORD			infosize,     /* Size of bitmap info */
					datasize,      /* Size of bitmap pixels */
					headersize;
    BITMAPFILEHEADER header;       /* File header */
	BITMAPINFO info;
	BOOL result=FALSE;
	int imgSize=image->getSize();
    if(fp&&image&&imgSize&&image->comps>=PIXEL_COMP_RGB)
	{
		datasize = image->length()*PIXEL_COMP_RGB;
		infosize = (DWORD)sizeof(BITMAPINFOHEADER);
		headersize = (DWORD)sizeof(BITMAPFILEHEADER);

		ClearMemory(&info,infosize);
		info.bmiHeader.biSize=infosize;
		info.bmiHeader.biWidth=image->width;
		info.bmiHeader.biHeight=image->height;
		info.bmiHeader.biBitCount=24;
		info.bmiHeader.biPlanes=1;

		ClearMemory(&header,headersize);
		header.bfType      = 'MB'; /* Non-portable... sigh */
		header.bfSize      = headersize + infosize + datasize;
		header.bfOffBits   = headersize + infosize;

		if (fwrite(&header, headersize, 1, fp) == 1&&
			fwrite(&info, infosize, 1, fp) == 1)
		{
			for(int i=2;i<imgSize;i+=image->comps)
			{
				fputc(image->data[i],fp);
				fputc(image->data[i-1],fp);
				fputc(image->data[i-2],fp);
			}
			result=TRUE;
		}
	}
    return result;
}
static inline void YUVtoRGB(const BYTE YUV[3], BYTE RGB[3])
{
	// U = Cb, V = Cr
	RGB[2] = (BYTE)clamp(__int(1.164f*(YUV[0] - 16)							+ 2.018f*(YUV[1] - 128)),	0, 255);
	RGB[1] = (BYTE)clamp(__int(1.164f*(YUV[0] - 16) - 0.813f*(YUV[2] - 128)	- 0.391f*(YUV[1] - 128)),	0, 255);
	RGB[0] = (BYTE)clamp(__int(1.164f*(YUV[0] - 16) + 1.596f*(YUV[2] - 128)),							0, 255);
}
BOOL readBitmap(BITMAPINFO *info, BYTE *data, DWORD, Image *image)
{
	if(!info || !image->setSize(info->bmiHeader.biWidth,info->bmiHeader.biHeight,PIXEL_COMP_RGB))
		return FALSE;
	if(info->bmiHeader.biBitCount<24)
	{
		switch(info->bmiHeader.biCompression)
		{
			case BI_RGB:
			{
				RGBQUAD *rgb;
				int index,bytes;
				bytes=(info->bmiHeader.biBitCount+7)/8;
				for(int i=0;i<image->length();i++)
				{
					index=readIntL(data,bytes,i*bytes);
					rgb=&info->bmiColors[index];
					image->data[3*i]=rgb->rgbRed;
					image->data[3*i+1]=rgb->rgbGreen;
					image->data[3*i+2]=rgb->rgbBlue;
				}
				return TRUE;
			}
			case 0x32595559: // YUY2
			{
				if(info->bmiHeader.biBitCount == 16 && image->width % 4 == 0)
				{
					BYTE YUV[3], *pixel;
					int n;
					for(int line=0;line<image->height;line++)
					{
						for(int col=0;col<image->width;col+=2)
						{
							pixel = data+2*(col+image->width*line);
							YUV[0] = pixel[0];
							YUV[1] = pixel[1];
							YUV[2] = pixel[3];
							n=3*(col+(image->height-line-1)*image->width);
							YUVtoRGB(YUV, image->data + n);
							YUV[0] = pixel[2];
							YUVtoRGB(YUV, image->data + n + 3);
						}
					}
					return TRUE;
				}
			}
		}
	}
	else
	{
		if(image->replace(data))
		{
			image->flipC();
			return TRUE;
		}
	}
	return FALSE;
}
BOOL readBitmap(FILE * fp, Image *image)
{
	BITMAPINFO		*info;
	BYTE			*data;
	DWORD			datasize,     /* Size of bitmap */
					infosize,     /* Size of header information */
					headersize;
	BITMAPFILEHEADER header;       /* File header */
	BOOL freeData=TRUE,result=FALSE;
	if(fp&&image)
	{
		headersize=(DWORD)sizeof(BITMAPFILEHEADER);
		if(	fread(&header, headersize, 1, fp)==1&&
			header.bfType == 'MB')
		{
			infosize = header.bfOffBits - headersize;
			if(NEW_MEMORY(info,BITMAPINFO,infosize))
			{
				if(fread(info, 1, infosize, fp)==infosize)
				{
					datasize=info->bmiHeader.biSizeImage;
					if (!datasize)
					{
						int width=info->bmiHeader.biWidth;
						int height=abs(info->bmiHeader.biHeight);
						int bpp=info->bmiHeader.biBitCount;
						int bytes=(bpp+7)/8;
						datasize=width*height*bytes;
					}
					if(NEW_ARRAY(data,BYTE,datasize))
					{
						if (fread(data, 1, datasize, fp)==datasize)
						{
							if(info->bmiHeader.biBitCount<24)
							{
								if(image->setSize(info->bmiHeader.biWidth,info->bmiHeader.biHeight,PIXEL_COMP_RGB))
								{
									RGBQUAD *rgb;
									int index,bytes;
									bytes=(info->bmiHeader.biBitCount+7)/8;
									for(int i=0;i<image->length();i++)
									{
										index=readIntL(data,bytes,i*bytes);
										rgb=&info->bmiColors[index];
										image->data[3*i]=rgb->rgbRed;
										image->data[3*i+1]=rgb->rgbGreen;
										image->data[3*i+2]=rgb->rgbBlue;
									}
									result=TRUE;
								}
							}
							else
							{
								if(image->bind(data,info->bmiHeader.biWidth,info->bmiHeader.biHeight,MEM_TYPE_NEW))
								{
									image->flipC();
									freeData=FALSE;
									result=TRUE;
								}
							}
						}
						if(freeData)
							DELETE_ARRAY(data);
					}
				}
				DELETE_MEMORY(info);
			}
		}
	}
	return result;
}

BOOL writeJPEG(	FILE * file,
				Image *image,
				int quality)
{
	struct jpeg_compress_struct info;
	struct jpeg_error_mgr error;
	int imgSize=image->getSize();
	if(!image||!imgSize||!file)
		return FALSE;
	J_COLOR_SPACE color_space;
	switch(image->comps)
	{
	case 1:
		color_space=JCS_GRAYSCALE;
		break;
	case 3:
		color_space=JCS_RGB;
		break;
	default:
		return FALSE;
	}
	try
	{
		info.err = jpeg_std_error(&error);
		jpeg_create_compress(&info);
		info.image_width=image->width;
		info.image_height=image->height;
		info.input_components=image->comps;
		info.in_color_space=color_space;
		jpeg_stdio_dest(&info, file);
		jpeg_set_defaults(&info);
		jpeg_set_quality(&info, quality, TRUE);
		JSAMPROW row_pointer[1];
		int row_stride=image->width*3;
		int size=image->height*row_stride;
		jpeg_start_compress(&info, TRUE);
		while (info.next_scanline < info.image_height)
		{
			row_pointer[0] = & image->data[size-(info.next_scanline+1) * row_stride];
			jpeg_write_scanlines(&info, row_pointer, 1);
		}
		jpeg_finish_compress(&info);
		jpeg_destroy_compress(&info);
	}
	catch(...)
	{
		return FALSE;
	}
	return TRUE;
}
BOOL readJPEG(	FILE * file,
				Image *image)
{
	struct jpeg_decompress_struct info;
	struct jpeg_error_mgr error;
	BOOL result=FALSE;
	if(image&&file)try
	{
		info.err = jpeg_std_error(&error);
		jpeg_create_decompress(&info);
		jpeg_stdio_src(&info, file);
		jpeg_read_header(&info, TRUE);
		int row_stride=info.image_width*PIXEL_COMP_RGB;
		int size=info.image_height*row_stride;
		if(image->setSize(info.image_width,info.image_height,PIXEL_COMP_RGB))
		{
			JSAMPROW row_pointer[1];
			jpeg_start_decompress(&info);
			while (info.output_scanline < info.image_height)
			{
				row_pointer[0] = & image->data[size-(info.output_scanline+1) * row_stride];
				jpeg_read_scanlines(&info, row_pointer, 1);
			}
			jpeg_finish_decompress(&info);
			result=TRUE;
		}
		jpeg_destroy_decompress(&info);
	}
	catch(...)
	{
		return FALSE;
	}
	return result;
}
void Image::clear()
{
	buffer.clear();
}
void Image::setPixel(DWORD color, int x, int y)
{
	BYTE *rgb,*pixel;
	rgb=INT_TO_BYTE(color);
	pixel=data+comps*(x+y*width);
	switch(comps)
	{
	case PIXEL_COMP_RGBA:
		pixel[ALPHA]=rgb[ALPHA];
	case PIXEL_COMP_RGB:
		pixel[BLUE]=rgb[BLUE];
		pixel[GREEN]=rgb[GREEN];
	case PIXEL_COMP_GRAY:
		pixel[RED]=rgb[RED];
	}
}
int Image::getPixel(int x, int y)const
{
	DWORD color=INT_BLACK;
	BYTE *pixel=data+comps*(x+y*width);
	switch(comps)
	{
	case PIXEL_COMP_RGBA:
		color=pixel[ALPHA]<<24;
	case PIXEL_COMP_RGB:
		color|=pixel[BLUE]<<16;
		color|=pixel[GREEN]<<8;
	case PIXEL_COMP_GRAY:
		color|=pixel[RED];
	}
	return color;
}
void Image::negative()
{
}
void Image::flipC()
{
	if(comps<PIXEL_COMP_RGB)
		return;
	const int s=width*height;
	const void *d=data;
	__asm							// Assembler Code To Follow
	{
		mov ecx, s					// Counter Set To Dimensions Of Our Memory Block
		mov ebx, d					// Points ebx To Our Data (b)
		label:						// Label Used For Looping
		mov al,[ebx+0]				// Loads Value At ebx Into al
		mov ah,[ebx+2]				// Loads Value At ebx+2 Into ah
		mov [ebx+2],al				// Stores Value In al At ebx+2
		mov [ebx+0],ah				// Stores Value In ah At ebx
		
		add ebx,3					// Moves Through The Data
		dec ecx						// Decreases Our Loop Counter
		jnz label					// If Not Zero Jump Back To Label
	}
}
void Image::flipV()
{
	register int my=height/2,n1,n2,i,j;
	for(i=0;i<width;i++)
	{
		for(j=0;j<my;j++)
		{
			n1=comps*(i+j*width);
			n2=comps*(i+(height-j-1)*width);
			switch(comps)
			{
			case PIXEL_COMP_RGBA:
				REPLACE(data[n1+3],data[n2+3]);
			case PIXEL_COMP_RGB:
				REPLACE(data[n1+2],data[n2+2]);
				REPLACE(data[n1+1],data[n2+1]);
			case PIXEL_COMP_GRAY:
				REPLACE(data[n1],data[n2]);
			}
		}
	}
}
void Image::flipH()
{
	register int mx=width/2,n1,n2,i,j;
	for(i=0;i<mx;i++)
	{
		for(j=0;j<height;j++)
		{
			n1=comps*(i+j*width);
			n2=comps*((width-i-1)+j*width);
			switch(comps)
			{
			case PIXEL_COMP_RGBA:
				REPLACE(data[n1+3],data[n2+3]);
			case PIXEL_COMP_RGB:
				REPLACE(data[n1+2],data[n2+2]);
				REPLACE(data[n1+1],data[n2+1]);
			case PIXEL_COMP_GRAY:
				REPLACE(data[n1],data[n2]);
			}
		}
	}
}
BOOL Image::checkPixelComp(BYTE pixelComp)
{
	switch(pixelComp)
	{
	case PIXEL_COMP_GRAY:
	case PIXEL_COMP_RGB:
	case PIXEL_COMP_RGBA:
		return TRUE;
	}
	return FALSE;
}

}; // namespace Lio